﻿Public Class ServerForm
    Dim ServerConsoleLines As New List(Of String)
    Friend Minimized As Boolean = False
    Dim MinimizedClose As Boolean = False
    Dim ShowBalloonTips As Boolean = True
    Friend ActionsToInvoke As New Dictionary(Of System.Delegate, Object)
    Friend TracesToAdd As New Queue(Of TreeNode)

    Public Sub New()
        InitializeComponent()
        Me.CreateHandle()
        UpdateTitle("MPQ Server")
    End Sub

    Protected Overloads Overrides Sub OnLoad(ByVal args As EventArgs)
        MyBase.OnLoad(args)
        AddHandler Application.Idle, AddressOf OnLoaded
    End Sub
    Private Sub OnLoaded(ByVal sender As Object, ByVal args As EventArgs)
        'If My.Application.CommandLineArgs.Contains("/a") Then
        '    Minimized = True
        '    Me.Hide()
        'End If
        If SingleGameOnly Then
            Me.RemoveControl(CreateGameButton)
        End If
        RemoveHandler Application.Idle, AddressOf OnLoaded
    End Sub

    Dim LastDetailStatus As String
    Dim LastDetail As String
    Dim StatusToAdd As New Queue(Of Status)

    Dim AddingStatus As Boolean = False

    Public Sub SetStatus(ByVal Status As String, Optional ByVal Detail As String = "")
        StatusToAdd.Enqueue(New Status With {.Status = Status, .Detail = Detail})
        If Not AddingStatus Then
            AddingStatus = True
            _SetStatus()
        End If
    End Sub

    Public Sub _SetStatus()
        Dim Status As String = ""
        Dim Detail As String = ""
        Do Until StatusToAdd.Count = 0
            Dim _Status As Status = StatusToAdd.Dequeue()
            Status = _Status.Status
            Detail = _Status.Detail

            If LastDetailStatus = Status Then
                With ServerConsoleLines
                    .Item(.LastIndexOf(Status & " (" & LastDetail & ")")) = Status & " (" & Detail & ")"
                End With
            Else
                If Detail = "" Then
                    ServerConsoleLines.Add(Status)
                Else
                    ServerConsoleLines.Add(Status & " (" & Detail & ")")
                End If
            End If
            If ServerConsoleLines.Count = 51 Then
                ServerConsoleLines.RemoveAt(0)
            End If
            If Not Detail = "" Then
                LastDetailStatus = Status
                LastDetail = Detail
            End If
        Loop
        SetConsoleLines()
        If Minimized And ShowBalloonTips Then
            ShowBalloonStatus()
        End If
        AddingStatus = False
    End Sub

    Structure Status
        Dim Status As String
        Dim Detail As String
    End Structure

    Sub ShowBalloonStatus()
        Dim Text As String = ""
        If ServerConsoleLines.Count > 6 Then
            For i As Integer = ServerConsoleLines.Count - 6 To ServerConsoleLines.Count - 1
                Text += ServerConsoleLines(i) & vbCr
            Next
        Else
            For i As Integer = 0 To ServerConsoleLines.Count - 1
                Text += ServerConsoleLines(i) & vbCr
            Next
        End If
        With My.Application.Info.Version
            NotifyIcon.ShowBalloonTip(5000, Me.Text.Replace(" - ver" & .Major & "." & .Minor & "." & .Build & "." & .Revision, ""), Text, ToolTipIcon.None)
        End With
    End Sub

    Public Sub SetConsoleLines()
        If InvokeRequired Then
            Try
                Invoke(New Action(AddressOf SetConsoleLines))
            Catch
            End Try
            Exit Sub
        End If
        ServerConsole.Lines = ServerConsoleLines.ToArray
        ServerConsole.SelectionStart = ServerConsole.Text.Length
        ServerConsole.ScrollToCaret()
    End Sub

    Public Sub AddControl(ByVal Control As Control)
        If InvokeRequired Then
            Try
                Invoke(New Action(Of Control)(AddressOf AddControl), Control)
            Catch
                ActionsToInvoke.Add(New Action(Of Control)(AddressOf AddControl), Control)
            End Try
            Exit Sub
        End If
        GamesPanel.Controls.Add(Control)
    End Sub

    Public Sub RemoveControl(ByVal Control As Control)
        If InvokeRequired Then
            Try
                Invoke(New Action(Of Control)(AddressOf RemoveControl), Control)
            Catch
                ActionsToInvoke.Add(New Action(Of Control)(AddressOf RemoveControl), Control)
            End Try
            Exit Sub
        End If
        Try
            GamesPanel.Controls.Remove(Control)
        Catch
        End Try
    End Sub

    Private Sub ServerForm_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        If Minimized And Not MinimizedClose Then
            e.Cancel = True
            Exit Sub
        End If
        Try
            If Not ServerMod.MainServer.State = ServiceModel.CommunicationState.Closed Then
                If Not e Is Nothing Then e.Cancel = True
                SetStatus("Closing Server...")
                SetStatus("Please wait")
                Try
                    ServerMod.MainServer.BeginClose(AddressOf ServerCloseCallback, "")
                Catch ex As Exception
                    SetStatus("Error:")
                    SetStatus(ex.Message)
                    e.Cancel = False
                    Application.Exit()
                End Try
            End If
        Catch
        End Try
        Invoke(New Action(AddressOf ReportViewer.Dispose))
    End Sub

    Sub ServerCloseCallback()
        Application.Exit()
    End Sub

    Private Sub CreateGameButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CreateGameButton.Click
generateID: Dim ID As Integer = (New Random).Next
        If GameServer.Games.Keys.Contains(ID) Then
            GoTo generateID
        End If
        Dim GameName As String = InputBox("Please enter a name for the new game")
        If GameName = "" Then Exit Sub
        ServerMod.GameServer.CreateGame(ID, GameName)
    End Sub

    Private Sub MainPanel_ControlAdded(ByVal sender As Object, ByVal e As System.Windows.Forms.ControlEventArgs) Handles GamesPanel.ControlAdded
        Try
            GamesPanel.Controls.SetChildIndex(CreateGameButton, GamesPanel.Controls.Count - 1)
        Catch
        End Try
    End Sub

    Dim SecondTimer As New Threading.Timer(New Threading.TimerCallback(AddressOf SecondTimer_Tick), Nothing, 1000, 1000)
    Private Sub SecondTimer_Tick()
        Try
            Invoke(New Action(AddressOf SecondTimerTick))
        Catch
        End Try
    End Sub

    Sub SecondTimerTick()
        Try
            If Not MainContext Is Nothing Then
                MainClientsLabel.Text = "Main Clients: " & MainContext.IncomingChannels.Count
            Else
                MainClientsLabel.Text = "Main Clients: 0"
            End If
            If Not DisplayServerI.Displays Is Nothing Then
                DisplayClientsLabel.Text = "Display Clients: " & DisplayServerI.Displays.Count
            End If
        Catch
        End Try
        Try
            MainClientsGauge.Value = MainContext.IncomingChannels.Count
        Catch
        End Try
        Try
            DisplayClientsGauge.Value = DisplayServerI.Displays.Count
        Catch
        End Try
        Try
            GamesGauge.Value = GameServer.Games.Count
        Catch
        End Try
        UpdateMainServerStatus()
        UpdateDisplayServerStatus()
        LastSaveStatusLabel.Text = "Last Save: " & GameServer.LastSave
        If Not ServerName = "" Then
            BaseURLStatusLabel.Text = "Base URL: " & PND.URITB.Text & "/MPQ/" & ServerName
        Else
            BaseURLStatusLabel.Text = "Base URL: " & PND.URITB.Text & "/MPQ/"
        End If

        For Each User As MPQService.ServerData.UsersRow In GameServer.ServerData.Users.Copy.Rows
            If Not UsersAndTeamsListView.Items.ContainsKey(User.Username) Then
                If Not User.IsTeamNull Then
                    UsersAndTeamsListView.Items.Add(New ListViewItem({User.Username, User.Score, User.Team, User.TeamsRow.Score, "Offline"}) With {.Name = User.Username})
                Else
                    UsersAndTeamsListView.Items.Add(New ListViewItem({User.Username, User.Score, "None", 0, "Offline"}) With {.Name = User.Username})
                End If
            End If
            If GameServer.LoggedInUsers.ContainsValue(User.Username) Then
                UsersAndTeamsListView.Items(User.Username).SubItems(4).Text = "Online"
            Else
                UsersAndTeamsListView.Items(User.Username).SubItems(4).Text = "Offline"
            End If
        Next

        If TraceTreeView.Visible Then
            AddTracesToTreeView()
        End If
    End Sub

    Sub UpdateMainServerStatus()
        Try
            If MainServer Is Nothing Then
                MainServerStatus.Value = 0
                MainServerStatus.StatusText = "No Server Object"
                Exit Try
            End If
            MainServerStatus.Value = 100
            Select Case MainServer.State
                Case ServiceModel.CommunicationState.Opened
                    MainServerStatus.ProgressState = ServerStatusControl.States.Normal
                    MainServerStatus.StatusText = "Server Running"
                Case ServiceModel.CommunicationState.Opening
                    MainServerStatus.ProgressState = ServerStatusControl.States.Paused
                    MainServerStatus.StatusText = "Starting..."
                Case ServiceModel.CommunicationState.Created
                    MainServerStatus.ProgressState = ServerStatusControl.States.Paused
                    MainServerStatus.StatusText = "Waiting to Start"
                Case ServiceModel.CommunicationState.Closing
                    MainServerStatus.ProgressState = ServerStatusControl.States.Error
                    MainServerStatus.StatusText = "Closed"
                Case ServiceModel.CommunicationState.Closed
                    MainServerStatus.ProgressState = ServerStatusControl.States.Error
                    MainServerStatus.StatusText = "Closed"
                Case ServiceModel.CommunicationState.Faulted
                    MainServerStatus.ProgressState = ServerStatusControl.States.Error
                    MainServerStatus.StatusText = "Faulted"
            End Select
        Catch
        End Try
    End Sub

    Sub UpdateDisplayServerStatus()
        Try
            If DisplayServer Is Nothing Then
                DisplayServerStatus.Value = 0
                DisplayServerStatus.StatusText = "No Server Object"
                Exit Try
            End If
            DisplayServerStatus.Value = 100
            Select Case DisplayServer.State
                Case ServiceModel.CommunicationState.Opened
                    DisplayServerStatus.ProgressState = ServerStatusControl.States.Normal
                    DisplayServerStatus.StatusText = "Server Running"
                Case ServiceModel.CommunicationState.Opening
                    DisplayServerStatus.ProgressState = ServerStatusControl.States.Paused
                    DisplayServerStatus.StatusText = "Starting..."
                Case ServiceModel.CommunicationState.Created
                    DisplayServerStatus.ProgressState = ServerStatusControl.States.Paused
                    DisplayServerStatus.StatusText = "Waiting to Start"
                Case ServiceModel.CommunicationState.Closing
                    DisplayServerStatus.ProgressState = ServerStatusControl.States.Error
                    DisplayServerStatus.StatusText = "Closed"
                Case ServiceModel.CommunicationState.Closed
                    DisplayServerStatus.ProgressState = ServerStatusControl.States.Error
                    DisplayServerStatus.StatusText = "Closed"
                Case ServiceModel.CommunicationState.Faulted
                    DisplayServerStatus.ProgressState = ServerStatusControl.States.Error
                    DisplayServerStatus.StatusText = "Faulted"
            End Select
        Catch
        End Try
    End Sub

    Private Sub ServerForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        ReportViewer.LocalReport.DataSources(0) = New Microsoft.Reporting.WinForms.ReportDataSource(ReportViewer.LocalReport.DataSources(0).Name, GameServer.ServerData.Usage_Data.Copy)
        ReportViewer.RefreshReport()
    End Sub

    Private Sub ServerForm_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
        If WindowState = FormWindowState.Minimized Then
            Minimized = True
            Me.Hide()
        End If

        If TabControl.Width > MainClientsGauge.Width * 2 + DisplayClientsGauge.Width * 2 And TabControl.Height > MainClientsGauge.Height * 2 + GamesGauge.Height * 2 Then
            GaugesTable.ColumnStyles(1).Width = GaugesTable.ColumnStyles(1).Width * 2
            GaugesTable.ColumnStyles(2).Width = GaugesTable.ColumnStyles(2).Width * 2
            GaugesTable.ColumnStyles(3).Width = GaugesTable.ColumnStyles(3).Width * 2
            GaugesTable.ColumnStyles(4).Width = GaugesTable.ColumnStyles(4).Width * 2

            GaugesTable.RowStyles(1).Height = GaugesTable.RowStyles(1).Height * 2
            GaugesTable.RowStyles(2).Height = GaugesTable.RowStyles(2).Height * 2

            ResizeGauge(MainClientsGauge, 2, True)
            ResizeGauge(DisplayClientsGauge, 2, True)
            ResizeGauge(GamesGauge, 2, True)
        End If

        If TabControl.Width < MainClientsGauge.Width + DisplayClientsGauge.Width And TabControl.Height < MainClientsGauge.Height + GamesGauge.Height Then
            GaugesTable.ColumnStyles(1).Width = GaugesTable.ColumnStyles(1).Width * 0.5
            GaugesTable.ColumnStyles(2).Width = GaugesTable.ColumnStyles(2).Width * 0.5
            GaugesTable.ColumnStyles(3).Width = GaugesTable.ColumnStyles(3).Width * 0.5
            GaugesTable.ColumnStyles(4).Width = GaugesTable.ColumnStyles(4).Width * 0.5

            GaugesTable.RowStyles(1).Height = GaugesTable.RowStyles(1).Height * 0.5
            GaugesTable.RowStyles(2).Height = GaugesTable.RowStyles(2).Height * 0.5

            ResizeGauge(MainClientsGauge, 2, False)
            ResizeGauge(DisplayClientsGauge, 2, False)
            ResizeGauge(GamesGauge, 2, False)
        End If
    End Sub

    Sub ResizeGauge(ByVal Gauge As AGauge.AGauge, ByVal Scale As Double, ByVal ScaleUp As Boolean)
        If Scale <= 1 Then Exit Sub
        With Gauge
            If Not ScaleUp Then
                Scale = 1 / Scale
            End If

            Dim CapOffset As Double = .Center.X - .CapPosition.X

            .Center = New Drawing.Point(.Center.X * Scale, .Center.Y * Scale)
            .CapPosition = New Drawing.Point(.Center.X - CapOffset, .CapPosition.Y * Scale)

            .BaseArcRadius = .BaseArcRadius * Scale
            .NeedleRadius = .NeedleRadius * Scale

            For i = 0 To 2
                .Range_Idx = i
                .RangeInnerRadius = .RangeInnerRadius * Scale
                .RangeOuterRadius = .RangeOuterRadius * Scale
            Next

            .ScaleLinesInterInnerRadius = .ScaleLinesInterInnerRadius * Scale
            .ScaleLinesInterOuterRadius = .ScaleLinesInterOuterRadius * Scale
            .ScaleLinesMajorInnerRadius = .ScaleLinesMajorInnerRadius * Scale
            .ScaleLinesMajorOuterRadius = .ScaleLinesMajorOuterRadius * Scale
            .ScaleLinesMinorInnerRadius = .ScaleLinesMinorInnerRadius * Scale
            .ScaleLinesMinorOuterRadius = .ScaleLinesMinorOuterRadius * Scale
            .ScaleNumbersRadius = .ScaleNumbersRadius * Scale
        End With
    End Sub

    Private Sub NotifyIcon_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles NotifyIcon.DoubleClick
        If Minimized Then
            Minimized = False
            Me.Show()
            Me.WindowState = FormWindowState.Normal
            SetConsoleLines()
            For Each item As KeyValuePair(Of System.Delegate, Object) In ActionsToInvoke
                Invoke(item.Key, item.Value)
            Next
            ActionsToInvoke.Clear()
            For Each Control As KeyValuePair(Of Integer, GameControl) In Games
                Control.Value.InvokeDelayedActions()
            Next
        End If
    End Sub

    Private Sub CloseMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CloseMenuItem.Click
        MinimizedClose = True
        ServerForm_FormClosing(Nothing, Nothing)
    End Sub

    Private Sub ShowStatusBalloonsMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ShowStatusBalloonsMenuItem.Click
        If ShowStatusBalloonsMenuItem.Checked = True Then
            ShowStatusBalloonsMenuItem.Checked = False
            ShowBalloonTips = False
        Else
            ShowStatusBalloonsMenuItem.Checked = True
            ShowBalloonTips = True
        End If
    End Sub

    Private Sub AddServerMessageButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddServerMessageButton.Click
        Dim ServerMessageDialog As New ServerMessageDialog
        If ServerMessageDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then
            GameServer.ServerMessages.Add(ServerMessageDialog.Message)
        End If
        ServerMessageDialog.Dispose()
        RefreshMessageList()
    End Sub

    Private Sub ServerMessageList_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles ServerMessageList.DoubleClick
        If ServerMessageList.SelectedIndex <> -1 Then
            Dim ServerMessageDialog As New ServerMessageDialog
            ServerMessageDialog.Message = GameServer.ServerMessages(ServerMessageList.SelectedIndex)
            If ServerMessageDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then
                GameServer.ServerMessages(ServerMessageList.SelectedIndex) = ServerMessageDialog.Message
            End If
            ServerMessageDialog.Dispose()
        End If
        RefreshMessageList()
    End Sub

    Sub RefreshMessageList()
        ServerMessageList.Items.Clear()
        For Each item As MPQService.ServerMessage In GameServer.ServerMessages
            ServerMessageList.Items.Add(item.Title)
        Next
    End Sub

    Public Sub UpdateTitle(ByVal NewTitle As String)
        If InvokeRequired = True Then
            Invoke(New Action(Of String)(AddressOf UpdateTitle), NewTitle)
            Exit Sub
        End If

        With My.Application.Info.Version
            Me.Text = NewTitle & " - ver" & .Major & "." & .Minor & "." & .Build & "." & .Revision
        End With
        NotifyIcon.Text = NewTitle
    End Sub

    Private Sub ReportViewer_ReportRefresh(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles ReportViewer.ReportRefresh
        ReportViewer.LocalReport.DataSources(0) = New Microsoft.Reporting.WinForms.ReportDataSource(ReportViewer.LocalReport.DataSources(0).Name, GameServer.ServerData.Usage_Data.Copy)
    End Sub

    Private Sub DeleteUserToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteUserToolStripMenuItem.Click
        Dim ItemsToDelete(UsersAndTeamsListView.SelectedItems.Count - 1) As ListViewItem
        UsersAndTeamsListView.SelectedItems.CopyTo(ItemsToDelete, 0)
        For Each item As ListViewItem In ItemsToDelete
            Dim Username As String = item.Text
            Dim query = From users In GameServer.ServerData.Users Where users.Username = Username
            GameServer.ServerData.Users.Rows.Remove(query(0))
            UsersAndTeamsListView.Items.Remove(item)
        Next
    End Sub

    Private Sub UsersAndTeamsMenu_Opening(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles UsersAndTeamsMenu.Opening
        Select Case UsersAndTeamsListView.SelectedItems.Count
            Case 0
                DeleteUserToolStripMenuItem.Enabled = False
            Case 1
                DeleteUserToolStripMenuItem.Text = "Delete User"
            Case Is > 1
                DeleteUserToolStripMenuItem.Text = "Delete Users"
        End Select
    End Sub

    Private Sub RefreshListToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RefreshListToolStripMenuItem.Click
        UsersAndTeamsListView.Items.Clear()
    End Sub

    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

    Sub AddTracesToTreeView()
        Do Until TracesToAdd.Count = 0
            TraceTreeView.Nodes.Add(TracesToAdd.Dequeue)
        Loop
        If Not TraceTreeView.Focused Then
            Dim LastNode As TreeNode = TraceTreeView.Nodes(TraceTreeView.Nodes.Count - 1)
            Dim Loops As Integer = 0
            Do Until LastNode.IsVisible Or Loops > 500
                SendMessage(GUI.TraceTreeView.Handle, 277&, 1&, vbNull)
                LastNode = TraceTreeView.Nodes(TraceTreeView.Nodes.Count - 1)
                Loops += 1
            Loop
            SendMessage(GUI.TraceTreeView.Handle, 277&, 1&, vbNull)
            If Loops > 500 Then
                TraceTreeView.SelectedNode = LastNode
            End If
        End If
    End Sub

    Private Sub ClearToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ClearToolStripMenuItem.Click
        TraceTreeView.Nodes.Clear()
    End Sub

    Private Sub TabControl_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TabControl.SelectedIndexChanged
        If TabControl.SelectedTab.Text = "Games" Then
            For Each Control As Control In GamesPanel.Controls
                Try
                    With CType(Control, GameControl)
                        .Console.SelectionStart = .Console.Text.Length
                    End With
                Catch ex As Exception

                End Try
            Next
        End If
    End Sub
End Class